package com.swim.pay.web;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.ijpay.core.kit.WxPayKit;
import com.swim.common.utils.SmsUtil;
import com.swim.natatoriumorder.entity.NatatoriumOrder;
import com.swim.natatoriumorder.service.NatatoriumOrderService;
import com.swim.pay.bean.MiniPayParam;
import com.swim.pay.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Random;

import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;

/**
 * Copyright 2023 www.szyypx.com
 *
 * @Description: 微信小程序支付控制器
 * @author: sz
 * @version: 1.0.0
 * @date: 2023/05/08
 */
@Slf4j
@RestController
@RequestMapping(value = "${adminPath}/miniappPay")
public class WxMiniappPayController {

    @Autowired
    private WxPayService wxService;
    @Autowired
    private NatatoriumOrderService natatoriumOrderService;
    @Autowired
    private UserInfoService userInfoService;


    /**
     * 功能描述: 小程序登录
     *
     * @param code 代码
     * @return {@link WxMaJscode2SessionResult }
     * @throws Exception 异常
     * @author: 石振
     * @version: 1.0.0
     * @date: 2023/05/14
     */
    @GetMapping("/login")
    public WxMaJscode2SessionResult login(@RequestParam(name = "code") String code) throws Exception {
        WxMaJscode2SessionResult result = userInfoService.login(code);
        log.info("通过code获取小程序登录用户信息，{}", JSONObject.toJSONString(result));
        return result;
    }

    /**
     * 功能描述: 统一下单
     *
     * @param param 订单号
     * @return {@link WxPayUnifiedOrderResult }
     * @throws Exception 异常
     * @author: 石振
     * @version: 1.0.0
     * @date: 2023/05/08
     */
    @PostMapping(value = "/unifiedOrder", headers = {"content-type=application/x-www-form-urlencoded"})
    public JSONObject unifiedOrder(MiniPayParam param, HttpServletRequest request) throws Exception {
        log.info("下单参数={}", JSONObject.toJSONString(param));
        String openid = param.getOpenid();
        if (StringUtils.isNotEmpty(param.getCode()) && StringUtils.isEmpty(openid)) {
            WxMaJscode2SessionResult result = userInfoService.login(param.getCode());
            log.info("通过code获取小程序登录用户信息，{}", JSONObject.toJSONString(result));
            openid = result.getOpenid();
        }
        WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
        wxPayUnifiedOrderRequest.setBody("小程序测试下单");
        wxPayUnifiedOrderRequest.setOutTradeNo(WxPayKit.generateStr());
        wxPayUnifiedOrderRequest.setTotalFee(Convert.toInt(param.getTotalFee() * 100, 1));
        wxPayUnifiedOrderRequest.setSpbillCreateIp(getIpAddr(request));
        wxPayUnifiedOrderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
        wxPayUnifiedOrderRequest.setOpenid(openid);
        wxPayUnifiedOrderRequest.setSignType(WxPayConstants.SignType.MD5);
        log.debug("小程序支付下单响应参数：{}", JSONObject.toJSONString(wxPayUnifiedOrderRequest));
        WxPayUnifiedOrderResult orderResult = wxService.unifiedOrder(wxPayUnifiedOrderRequest);
//        logger.debug("小程序支付下单响应结果：{}",JSONObject.toJSONString(orderResult));
//        return orderResult;
        JSONObject jsonObject = new JSONObject();
        String appId = orderResult.getAppid();
        String prepay_id = orderResult.getPrepayId();//返回的预付单信息
        String nonceStr = orderResult.getNonceStr();
        jsonObject.put("nonceStr", nonceStr);
        jsonObject.put("package", "prepay_id=" + prepay_id);
        Long timeStamp = System.currentTimeMillis() / 1000;
        String stringSignTemp = "appId=" + appId + "&nonceStr=" + nonceStr + "&package=prepay_id=" + prepay_id + "&signType=" + "MD5" + "&timeStamp=" + timeStamp;
        String paySign = sign(stringSignTemp, "YBy4MItTte3APraHjMgG5DO8eqEd5nDX", "utf-8").toUpperCase();
        log.error("=======================第二次签名：{}=====================", paySign);
        jsonObject.put("timeStamp", timeStamp + "");
        jsonObject.put("paySign", paySign);
        return jsonObject;
    }

    /**
     * 获取用户实际ip
     *
     * @param request
     * @return
     */
    public String getIpAddr(HttpServletRequest request) {
        String ipAddress = request.getHeader("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                //根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    log.error(e.getMessage(), e);
                }
                ipAddress = inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }

    /**
     * 功能描述:
     * 功能描述: 查询订单
     *
     * @param outTradeNo 订单号
     * @return {@link WxPayOrderQueryResult }
     * @throws Exception 异常
     * @author: 石振
     * @version: 1.0.0
     * @date: 2023/05/08
     */
    @PostMapping("/queryOrder")
    public WxPayOrderQueryResult queryOrder(String outTradeNo) throws Exception {
        WxPayOrderQueryResult wxPayOrderQueryResult = wxService.queryOrder(null, outTradeNo);
        log.info(wxPayOrderQueryResult.toString());
        return wxPayOrderQueryResult;
    }


    @PostMapping("notify")
    public String notifyOrder(@RequestBody String xmlData) throws WxPayException {
        log.info("支付回调：{}", xmlData);
        //微信支付服务
        WxPayOrderNotifyResult notifyResult = wxService.parseOrderNotifyResult(xmlData);
        log.info("支付回调解析结果{}", notifyResult);
        //对创建的订单进行支付
        // 结果正确 outTradeNo
        String orderId = notifyResult.getOutTradeNo();
        String tradeNo = notifyResult.getTransactionId();
        Integer totalFee = notifyResult.getTotalFee();
        //判断支付金额是否和消费记录中一致
        JSONObject result = new JSONObject();
        NatatoriumOrder natatoriumOrder = new NatatoriumOrder();
        natatoriumOrder.setOrderNum(orderId);
        List<NatatoriumOrder> orderList = natatoriumOrderService.findList(natatoriumOrder);
        NatatoriumOrder sysOrder = CollectionUtils.isNotEmpty(orderList) ? orderList.get(0) : null;
        if (sysOrder != null) {
            if (new BigDecimal(sysOrder.getShouldPay()).multiply(new BigDecimal(100)).intValue() == notifyResult.getTotalFee()) {
                orderPaySuccess(sysOrder);
                String timeEnd = notifyResult.getTimeEnd();

                //支付时间
                sysOrder.setPayDate(DateUtil.parse(timeEnd, NORM_DATETIME_PATTERN));
                sysOrder.setOrderNum(orderId);
                sysOrder.setPayNumber(tradeNo);
                //更新数据库
                natatoriumOrderService.update(sysOrder);
                log.info("支付回调成功通知：{}", JSONObject.toJSONString(notifyResult));
                return WxPayNotifyResponse.success("支付成功");
            } else {
                return WxPayNotifyResponse.fail("付款金额与订单金额不符");
            }
        } else {
            return WxPayNotifyResponse.fail("无此订单");
        }
    }

    /**
     * 更新订单支付状态(订单支付成功)
     *
     * @param natatoriumOrder
     * @return id
     */
    public void orderPaySuccess(NatatoriumOrder natatoriumOrder) {
        //付款成功后给用户发送核销验证码
        String code = "";
        try {
            Random random = new Random();
            for (int i = 0; i < 6; i++) {
                code += random.nextInt(10);
            }
            // 发送验证码
            SmsUtil.sendSettlementCode(natatoriumOrder.getOrderNum(), natatoriumOrder.getPayerPhone(), code);
        } catch (Exception e) {
            log.error("发送核销码短信异常");
        }
        //把核销验证码保存到订单中
        natatoriumOrder.setSettlementCode(code);
        //修改订单状态
        natatoriumOrder.setOrderStatus(2L);// 订单状态 1表示付款中 2表示已付款 3表示已核销 4表示已关闭
        if (natatoriumOrder.getNatatoriumGoods().getGoodsType() == 1L) {// 商品类型 1表示游泳票 2表示培训课程
            natatoriumOrder.setSettlementStatus(1L);        // 结算状态 1表示可结算(专指游泳票) 2表示不可结算 3表示可结算首款 4表示可结算尾款 5表示已结算首款 6表示已结算
        } else if (natatoriumOrder.getNatatoriumGoods().getGoodsType() == 2L) {
            natatoriumOrder.setSettlementStatus(3L);
        }
        natatoriumOrderService.update(natatoriumOrder);
    }

    private String sign(String text, String key, String input_charset) {
        text = text + "&key=" + key;
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));
    }

    private byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

}
